﻿#include "untility.h"
#include "errorcode.h"
#include <algorithm>
#include <chrono>
#include <ctime>

#include <sys/stat.h>
//#include <windows.h>

#ifdef GS_PLAT_LINUX
#include <cstring>
#include <unistd.h>
#endif

xdatetime x_current_time()
{
    xdatetime ret;
    // 使用std::chrono获取当前时间点
    auto now = std::chrono::system_clock::now();

    // 将时间点转换为time_t以便我们可以使用本地时间转换函数
    std::time_t now_time = std::chrono::system_clock::to_time_t(now);

    // 使用localtime转换time_t为本地时间
    std::tm loctm;// std::localtime(&now_time);
#ifdef GS_PLAT_WIN
    localtime_s(&loctm, &now_time);
#elif defined(GS_PLAT_LINUX)
   localtime_r(&now_time, &loctm);
#else
#error  Not supported: local time.
#endif

    ret.year = 1900 + loctm.tm_year;
    ret.month = 1 + (xuint1)loctm.tm_mon;
    ret.day = (xuint1)loctm.tm_mday;
    ret.dayOfWeek = (xuint1)loctm.tm_wday;

    ret.hour = (xuint1)loctm.tm_hour;
    ret.minute = (xuint1)loctm.tm_min;
    ret.second = (xuint1)loctm.tm_sec;

    return ret;
}

std::string x_ant_warning_prefix(xint code)
{
    char buf[128] = { '\0' };
#ifdef GS_PLAT_WIN
    sprintf_s(buf, sizeof(buf) / sizeof(buf[0]), "gsrev warning %d: ", code);    
#elif defined(GS_PLAT_LINUX)
    sprintf(buf, "gsrev warning %d: ", code);
#else
#error  Not supported: sprintf.
#endif
    
    return buf;
}
std::wstring x_ant_warning_wprefix(xint code)
{
    wchar_t buf[128] = { L'\0' };
#ifdef GS_PLAT_WIN
    swprintf_s(buf, sizeof(buf) / sizeof(buf[0]), L"gsrev warning %d: ", code);    
#elif defined(GS_PLAT_LINUX)
    wprintf(buf, L"gsrev warning %d: ", code);    
#else
#error  Not supported: wprintf.
#endif    
    return buf;
}
std::string x_ant_error_prefix(xint code)
{
    char buf[128] = { '\0' };
 #ifdef GS_PLAT_WIN
    sprintf_s(buf, sizeof(buf) / sizeof(buf[0]), "gsrev error %d: ", code);    
#elif defined(GS_PLAT_LINUX)
    sprintf(buf, "gsrev error %d: ", code);
#else
#error  Not supported: sprintf.
#endif   
    
    return buf;
}
std::wstring x_ant_error_wprefix(xint code)
{
    wchar_t buf[128] = { L'\0' };
#ifdef GS_PLAT_WIN
    swprintf_s(buf, sizeof(buf) / sizeof(buf[0]), L"gsrev error %d: ", code);    
#elif defined(GS_PLAT_LINUX)
    wprintf(buf, L"gsrev error %d: ", code);
#else
#error  Not supported: wprintf.
#endif
    
    return buf;
}

size_t x_str_utf16_2_ansi(char * buf, xint buf_size, const xchar * wstr)
{
    /* if (xnullptr == buf)
        return ::WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, FALSE);

    return ::WideCharToMultiByte(CP_ACP, 0, wstr, -1, buf, buf_size, NULL, FALSE);
    */
    size_t ret = 0;
#ifdef GS_PLAT_WIN
    wcstombs_s(&ret, buf, buf_size, wstr, buf_size - 1);    
#elif defined(GS_PLAT_LINUX)
    ret = wcstombs(buf, wstr, buf_size);
#else
#error  Not supported: wcstombs.
#endif
    
    return ret;
}

size_t x_str_ansi_2_utf16(wchar_t * buf, xint buf_size, const char * str)
{
    /*if (xnullptr == buf)
        return ::MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);

    return ::MultiByteToWideChar(CP_ACP, 0, str, -1, buf, buf_size);
    */
   size_t ret = 0;
#ifdef GS_PLAT_WIN
      mbstowcs_s(&ret, buf, buf_size, str, buf_size - 1);  
#elif defined(GS_PLAT_LINUX)
      ret = mbstowcs(buf, str, buf_size);
#else
#error  Not supported: mbstowcs.
#endif    
    
    return ret;
}

std::string x_str_replace_all(std::string str, const std::string& from, const std::string& to)
{
    size_t start_pos = 0;
    while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
        str.replace(start_pos, from.length(), to);
        start_pos += to.length(); // 防止无限循环，如果`to`包含`from`
    }
    return str;
}

std::string x_str_trim(const std::string & str)
{
    auto start = str.find_first_not_of(" \t\n\r\f\v");
    auto end = str.find_last_not_of(" \t\n\r\f\v");
    return (start != std::string::npos) ? str.substr(start, end - start + 1) : "";
}

std::wstring x_str_trim(const std::wstring & str)
{
    auto start = str.find_first_not_of(L" \t\n\r\f\v");
    auto end = str.find_last_not_of(L" \t\n\r\f\v");
    return (start != std::string::npos) ? str.substr(start, end - start + 1) : L"";
}

xint8 x_str_index_of(const char * buf, char c, xint8 from)
{
    if (xnullptr == buf)
        return -1;

    const char *cur = buf;
    if (-1 != from)
        cur = buf + from;

    while ('\0' != *cur)
    {
        if (c == *cur)
            return cur - buf;

        ++cur;
    }
    return -1;
}

std::string x_datetime_to_str(const xdatetime & dt)
{
    std::string strDate;
    //  2023/11/20 16:52:27
    char buf[256] = { '\0' };
#ifdef GS_PLAT_WIN
    sprintf_s(buf, sizeof(buf) / sizeof(buf[0]), "%d", dt.year);    
#elif defined(GS_PLAT_LINUX)
    sprintf(buf, "%d", dt.year);
#else
#error  Not supported: sprintf.
#endif
    
    strDate = buf;
    strDate += "-";

    buf[0] = '\0';
#ifdef GS_PLAT_WIN
    sprintf_s(buf, sizeof(buf) / sizeof(buf[0]), "%d", dt.month);    
#elif defined(GS_PLAT_LINUX)
    sprintf(buf, "%d", dt.month);
#else
#error  Not supported: sprintf.
#endif

    
    std::string strTmp;
    strTmp = buf;
    if (1 == strTmp.size())
        strDate += "0";
    strDate += strTmp;
    strDate += "-";

    buf[0] = '\0';
#ifdef GS_PLAT_WIN
    sprintf_s(buf, sizeof(buf) / sizeof(buf[0]), "%d", dt.day);    
#elif defined(GS_PLAT_LINUX)
    sprintf(buf, "%d", dt.day);
#else
#error  Not supported: sprintf.
#endif
    
    strTmp = buf;
    if (1 == strTmp.size())
        strDate += "0";
    strDate += strTmp;

    strDate += " ";

    buf[0] = '\0';
#ifdef GS_PLAT_WIN
    sprintf_s(buf, sizeof(buf) / sizeof(buf[0]), "%d", dt.hour);    
#elif defined(GS_PLAT_LINUX)
    sprintf(buf, "%d", dt.hour);
#else
#error  Not supported: sprintf.
#endif

    strTmp = buf;
    if (1 == strTmp.size())
        strDate += "0";
    strDate += strTmp;
    strDate += ":";

    buf[0] = '\0';
#ifdef GS_PLAT_WIN
    sprintf_s(buf, sizeof(buf) / sizeof(buf[0]), "%d", dt.minute);    
#elif defined(GS_PLAT_LINUX)
    sprintf(buf, "%d", dt.minute);
#else
#error  Not supported: sprintf.
#endif

    strTmp = buf;
    if (1 == strTmp.size())
        strDate += "0";
    strDate += strTmp;
    strDate += ":";

    buf[0] = '\0';
#ifdef GS_PLAT_WIN
    sprintf_s(buf, sizeof(buf) / sizeof(buf[0]), "%d", dt.second);        
#elif defined(GS_PLAT_LINUX)
    sprintf(buf, "%d", dt.second);
#else
#error  Not supported: sprintf.
#endif
    
    strTmp = buf;
    if (1 == strTmp.size())
        strDate += "0";
    strDate += strTmp;

    /*TIME_ZONE_INFORMATION  tzi;
    ::GetSystemTime(&tzi.StandardDate);
    ::GetTimeZoneInformation(&tzi);
    //https://blog.csdn.net/huaqianzkh/article/details/137240728
    xint zone = tzi.Bias / -60; //时区，如果是中国标准时间则得到8 
    */
    time_t zone = 0;
    {
        // 获取系统时间
        time_t _rt = time(NULL);
        // 系统时间转换为GMT时间
        tm _gtm; // = *gmtime(&_rt);
#ifdef GS_PLAT_WIN
        gmtime_s(&_gtm, &_rt);
#elif defined(GS_PLAT_LINUX)
        gmtime_r(&_rt, &_gtm);
#else
#error  Not supported: gmtime.
#endif

        // 系统时间转换为本地时间
        tm _ltm;// = *localtime(&_rt);
        
#ifdef GS_PLAT_WIN
        localtime_s(&_ltm, &_rt);
#elif defined(GS_PLAT_LINUX)
        localtime_r(&_rt, &_ltm);
#else
#error  Not supported: local time.
#endif
        // 再将GMT时间重新转换为系统时间
        time_t _gt = mktime(&_gtm);
        tm _gtm2;// = *localtime(&_gt);
        
#ifdef GS_PLAT_WIN
        localtime_s(&_gtm2, &_gt);
#elif defined(GS_PLAT_LINUX)
        localtime_r(&_gt, &_gtm2);
#else
#error  Not supported: local time.
#endif
        // 这时的_gt已经与实际的系统时间_rt有时区偏移了,计算两个值的之差就是时区偏的秒数,除60就是分钟
        zone = ((_rt - _gt) + (_gtm2.tm_isdst ? 3600 : 0)) / 3600;
    }
    buf[0] = '\0';
#ifdef GS_PLAT_WIN
    if (0 > int(zone)) {
        sprintf_s(buf, sizeof(buf) / sizeof(buf[0]), " -0%d00", zone);
    }
    else {
        sprintf_s(buf, sizeof(buf) / sizeof(buf[0]), " +0%d00", zone);
    }    
#elif defined(GS_PLAT_LINUX)
    if (0 > int(zone)) {
        sprintf(buf, " -0%d00", zone);
    }
    else {
        sprintf(buf, " +0%d00", zone);
    }
#else
#error  Not supported: sprintf.
#endif

   
    strDate += buf;


    return strDate;
}

std::string x_str_utc(xdatetime_t utc)
{
    std::string ver;
    char buf[256] = { '\0' };
#ifdef GS_PLAT_WIN
    sprintf_s(buf, sizeof(buf) / sizeof(buf[0]), "%lu", utc);    
#elif defined(GS_PLAT_LINUX)
    sprintf(buf, "%lu", utc);
#else
#error  Not supported: local time.
#endif
    
    ver = buf;
    return ver;
}

#ifndef S_ISDIR
#define S_ISDIR(m)  (((m) & S_IFMT) == S_IFDIR)
#endif // S_ISDIR

bool x_file_exists(const wchar_t *filename) 
{
    /*DWORD attributes = GetFileAttributesW(filename);
    return (attributes != INVALID_FILE_ATTRIBUTES) && !(attributes & FILE_ATTRIBUTE_DIRECTORY);*/

    char pathBuf[4096] = { '\0' };
    x_str_utf16_2_ansi(pathBuf, sizeof(pathBuf) / sizeof(pathBuf[0]), filename);

    struct stat buffer;
    if (0 == stat(pathBuf, &buffer))
    {
        if (S_ISDIR(buffer.st_mode))
            return false;

        return true; // 存在
    }

    return false;
}


bool x_path_exists(const wchar_t * path) 
{
    /*
    WIN32_FIND_DATAW fd;
    HANDLE hFind = ::FindFirstFileW(path, &fd);
    bool rt = INVALID_HANDLE_VALUE != hFind;
    ::FindClose(hFind);
    return rt;
    */

    char pathBuf[4096] = { '\0' };
    x_str_utf16_2_ansi(pathBuf, sizeof(pathBuf) / sizeof(pathBuf[0]), path);

    struct stat buffer;
    if (0 == stat(pathBuf, &buffer)) 
        return true; // 存在

    return false; // 不存在
}

bool x_utc_to_datetime(xdatetime_t utc, xdatetime & datetime)
{
    std::time_t val = utc;
    //// 获取本地时间的tm结构
    std::tm tm;
#ifdef GS_PLAT_WIN
    _localtime64_s(&tm, &val);    
#elif defined(GS_PLAT_LINUX)
    localtime_r(&val, &tm);
#else
#error  Not supported: local time.
#endif
    

    datetime.year = tm.tm_year + 1900;
    datetime.month = tm.tm_mon + 1;
    datetime.day = tm.tm_mday;

    datetime.hour = tm.tm_hour;
    datetime.minute = tm.tm_min;
    datetime.second = tm.tm_sec;
    datetime.milliSecond = 0;
    datetime.dayOfWeek = datetime.calcDayOfWeek(datetime.year, datetime.month, datetime.day);

    return true;
}


xint x_parse_cmdline(
    xint argc,
    char ** argv,
    bool &help,
    std::wstring & path,
    std::wstring &templateFile,
    std::vector<OutFile> & outFiles,
    xint & gH,
    xint & flag,
    xint & tortoise,
    xint & hasparams // 是否有参数
    )
{
    help = false;
    hasparams = 0;
    for (xint i = 0; i < argc; ++i)
    {
        std::string cmd = argv[i];

        if (!help && cmd == "-h")
        {
            help = true;
            hasparams = 1;
            break;
        }

        if (0 == gH && cmd == "-gH")
        {
            gH = 1;
            hasparams = 1;
            continue;
        }

        if (0 == tortoise && cmd == "-tortoise")
        {
            tortoise = 1;
            hasparams = 1;
            continue;
        }

        if (path.empty() && x_str_starts_with(cmd.c_str(), cmd.size(), "-p:", 3, '\0', x_char))
        {
            wchar_t buf[4096] = { L'\0' };
            x_str_ansi_2_utf16(buf, sizeof(buf) / sizeof(buf[0]), cmd.c_str() + 3);
            path = buf;
            hasparams = 1;
            continue;
        }

        if (templateFile.empty() && x_str_starts_with(cmd.c_str(), cmd.size(), "-t:", 3, '\0', x_char))
        {
            wchar_t buf[4096] = { L'\0' };
            x_str_ansi_2_utf16(buf, sizeof(buf) / sizeof(buf[0]), cmd.c_str() + 3);
            templateFile = buf;
            hasparams = 1;
            continue;
        }

        if (x_str_starts_with(cmd.c_str(), cmd.size(), "-og:", 4, '\0', x_char))
        {
            OutFile outFile;
            outFile.outType = OutType_Git;
            wchar_t buf[4096] = { L'\0' };
            x_str_ansi_2_utf16(buf, sizeof(buf) / sizeof(buf[0]), cmd.c_str() + 4);
            outFile.outFile = buf;
            outFiles.push_back(outFile);
            flag |= 0x1;
            hasparams = 1;
            continue;
        }

        if (x_str_starts_with(cmd.c_str(), cmd.size(), "-os:", 4, '\0', x_char))
        {
            OutFile outFile;
            outFile.outType = OutType_Svn;
            wchar_t buf[4096] = { L'\0' };
            x_str_ansi_2_utf16(buf, sizeof(buf) / sizeof(buf[0]), cmd.c_str() + 4);
            outFile.outFile = buf;
            outFiles.push_back(outFile);
            flag |= 0x2;
            hasparams = 1;
            continue;
        }

        if (x_str_starts_with(cmd.c_str(), cmd.size(), "-ogs:", 5, '\0', x_char))
        {
            OutFile outFile;
            outFile.outType = OutType_GitSvn;
            wchar_t buf[4096] = { L'\0' };
            x_str_ansi_2_utf16(buf, sizeof(buf) / sizeof(buf[0]), cmd.c_str() + 5);
            outFile.outFile = buf;
            outFiles.push_back(outFile);
            flag |= 0x3;
            hasparams = 1;
            continue;
        }

        if (x_str_starts_with(cmd.c_str(), cmd.size(), "-osg:", 5, '\0', x_char))
        {
            OutFile outFile;
            outFile.outType = OutType_SvnGit;
            wchar_t buf[4096] = { L'\0' };
            x_str_ansi_2_utf16(buf, sizeof(buf) / sizeof(buf[0]), cmd.c_str() + 5);
            outFile.outFile = buf;
            outFiles.push_back(outFile);
            flag |= 0x3;
            hasparams = 1;
            continue;
        }
    }
    if (help || 0 == hasparams)
        return 0;

    if (path.empty())
    {
        std::cout << "代码路径不能是空路径." << std::endl;
        return gsrev::ErrCode_PathEmpty;
    }

    if (!x_path_exists(path.c_str()))
    {
        std::cout << "代码路径不存在." << std::endl;
        return gsrev::ErrCode_PathNotExists;
    }

    if (templateFile.empty())
    {
        std::cout << "模板文件不能是空路径." << std::endl;
        return gsrev::ErrCode_TemplateEmpty;
    }
    if (!x_file_exists(templateFile.c_str()))
    {
        std::cout << "模板文件不存在." << std::endl;
        return gsrev::ErrCode_TemplateNotExists;
    }

    if (outFiles.empty())
    {
        std::cout << "输出文件不能是空路径." << std::endl;
        return gsrev::ErrCode_OutFileEmpty;
    }

    //std::sort(outFiles.begin(), outFiles.end());

    return gsrev::ErrCode_OK;
}

/// @brief 修改目录
/// @param path [>]修改当前的配置路径
/// @return 返回说明
/// @retval 0   修改成功
/// @retval 其他 修改失败
xint x_chdir(const wchar_t * path)
{
#ifdef GS_PLAT_WIN
    return _wchdir(path);    
#elif defined(GS_PLAT_LINUX)
    char filepath[4096] = { '\0' };    
    wcstombs(filepath, path, sizeof(filepath) / sizeof(filepath[0]));
    return chdir(filepath);
#else
#error  Not supported: isdir.
#endif
    return -1;
}

FILE* x_popen(const wchar_t* filename, const wchar_t* mode)
{
#ifdef GS_PLAT_WIN
    return _wpopen(filename, mode);       
#elif defined(GS_PLAT_LINUX)
    char m[256]= { '\0' };
    wcstombs(m, mode, sizeof(m) / sizeof(m[0]));
    char filepath[4096] = { '\0' };    
    wcstombs(filepath, filename, sizeof(filepath) / sizeof(filepath[0]));
    return popen(filepath, m);
#else
#error  Not supported: popen.
#endif
}
bool x_exec_cmd_by_pipe(wchar_t * szFetCmd, const wchar_t * path, char *szBuffer, size_t bufferSize, unsigned long & byteCount)
{
    szBuffer[0] = '\0';

    if (nullptr != path)
    {// 设置版本库的代码路径
        if (0 != x_chdir(path)) // chdir
            return false;
    }

    FILE *pipe = x_popen(szFetCmd, L"r");
    if (nullptr == pipe)
        return false;
    
    //fseek(pipe, 0, SEEK_END);
    
    size_t   bufSize = bufferSize;
    char *buf = fgets(szBuffer, bufSize, pipe);
    size_t len = 0;
    while (nullptr != buf)
    {
        len +=  strlen(buf);
        if (len >= bufferSize)
        {
            len = bufferSize;
            break;
        }
        buf = szBuffer + len;
        buf = fgets(buf, bufSize, pipe);
    }
#ifdef GS_PLAT_WIN
    _pclose(pipe);    
#elif defined(GS_PLAT_LINUX)
     pclose(pipe);
#else
#error  Not supported: pclose.
#endif
    

    if (0 < len && len < bufferSize)
        szBuffer[len] = '\0';

    byteCount = (unsigned long)len;
    return true;
}

/*
bool x_exec_cmd_by_pipe(wchar_t * szFetCmd, const wchar_t * path, char *szBuffer, xint bufferSize, unsigned long & byteCount)
{
    BOOL   bret = FALSE;
    HANDLE hReadPipe = NULL; //读取管道
    HANDLE hWritePipe = NULL; //写入管道	

    STARTUPINFO			si;	  //控制命令行窗口信息
    SECURITY_ATTRIBUTES sa;   //安全属性

    memset(&si, 0, sizeof(si));
    memset(&sa, 0, sizeof(sa));
    si.cb = sizeof(STARTUPINFO);
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;

    //1.0 创建管道
    bret = ::CreatePipe(&hReadPipe, &hWritePipe, &sa, 0);
    if (!bret)
    {
        ::CloseHandle(hReadPipe);
        ::CloseHandle(hWritePipe);
        return false;
    }

    //2.0 设置命令行窗口的信息为指定的读写管道
    ::GetStartupInfo(&si);
    si.hStdError = hWritePipe;
    si.hStdOutput = hWritePipe;
    si.wShowWindow = SW_HIDE; //隐藏命令行窗口
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;

    //3.0 创建获取命令行的进程
    PROCESS_INFORMATION pi;   //进程信息	
    memset(&pi, 0, sizeof(pi));
    pi.hProcess = NULL;
    pi.hThread = NULL;
    bret = ::CreateProcessW(NULL, szFetCmd, NULL, NULL, TRUE, 0, NULL, path, &si, &pi);

    auto closeHandle = [&]()->void
    {
        ::CloseHandle(hReadPipe);
        ::CloseHandle(hWritePipe);
        ::CloseHandle(pi.hProcess);
        ::CloseHandle(pi.hThread);
    };

    if (!bret)
    {
        closeHandle();
        return false;
    }

    //4.0 读取返回的数据
    ::WaitForSingleObject(pi.hProcess, 500); //500  INFINITE
    bret = ::ReadFile(hReadPipe, szBuffer, bufferSize, &byteCount, 0);
    if (!bret || '\0' == *szBuffer)
    {
        closeHandle();
        return false;
    }

    closeHandle();
    return true;
}
*/

xint x_code_git_info(const xparam & pm, xcodeinfo & codeInfo)
{
    xint rcode = gsrev::ErrCode_OK;
    char buf[65536] = { '\0' };
    unsigned long byteCnt = 0;
    bool ok = false;
    {// 获取git版本
        memset(buf, 0, sizeof(buf) / sizeof(buf[0]));
        byteCnt = 0;
        wchar_t cmd[] = L"git -v";
#ifdef X_OUT_CMD
        std::wcout << x_ant_wprefix() << cmd << std::endl;
#endif //X_OUT_CMD
        ok = x_exec_cmd_by_pipe(cmd, xnullptr, buf, sizeof(buf) / sizeof(buf[0]), byteCnt);
        if (!ok || '\0' == buf[0])
        {
            rcode = gsrev::ErrCode_FailedToGetGitVersion;
            std::cout << x_ant_warning_prefix(rcode) << "获取git版本号失败, 可能未安装git或未配置环境变量." << std::endl;
            codeInfo.result = 0;
            return rcode;
        }
        std::string txt(buf);
        bool hasEndl = false;
        if (!txt.empty())
            hasEndl = ('\n' == txt.at(txt.size() - 1));
        std::cout << txt; // 正确输出: git version 2.46.1.windows.1 , 否则输出git -v的错误信息
        if (!hasEndl)
            std::cout << std::endl;

        txt = x_str_trim(txt);
        if (!x_str_starts_with(txt.c_str(), txt.size(), "git", 3, '\0', x_tolower))
        {
            rcode = gsrev::ErrCode_FailedToGetGitVersion;
            std::cout << x_ant_warning_prefix(rcode) << "没有发现git命令." << std::endl;
            codeInfo.result = 0;
            return rcode;
        }
    }

    {// 获取时间戳
        memset(buf, 0, sizeof(buf) / sizeof(buf[0]));
        byteCnt = 0;
        wchar_t cmd[] = L"git log -1 --pretty=format:\"%at\""; // at 或 ct（提交日期）
#ifdef X_OUT_CMD
        std::wcout << x_ant_wprefix() << cmd << std::endl;
#endif //X_OUT_CMD
        ok = x_exec_cmd_by_pipe(cmd, pm.codePath.c_str(), buf, sizeof(buf) / sizeof(buf[0]), byteCnt);
        if (!ok || '\0' == buf[0])
        {
            rcode = gsrev::ErrCode_FailedToExecGitLogTime;
            std::cout << x_ant_warning_prefix(rcode) << "获取时间戳失败." << std::endl;
            codeInfo.result = 0;
            return rcode;
        }
        std::string stamp = buf;
        char *endPtr = nullptr;
        xint8 timestamp = strtoull(buf, &endPtr, 10);
        xdatetime dt;
        x_utc_to_datetime(timestamp, dt);
        codeInfo.datetime = x_datetime_to_str(dt);
    }

    {// 获取SHA-1
        memset(buf, 0, sizeof(buf) / sizeof(buf[0]));
        byteCnt = 0;
        //                  0123456789012345678901234567 890 1
        wchar_t cmd[40] = L"git log -1 --pretty=format:\"%H\"";
#ifdef X_OUT_CMD
        std::wcout << x_ant_wprefix() << cmd << std::endl;
#endif //X_OUT_CMD
        ok = x_exec_cmd_by_pipe(cmd, pm.codePath.c_str(), buf, sizeof(buf) / sizeof(buf[0]), byteCnt);
        if (!ok || '\0' == buf[0])
        {
            rcode = gsrev::ErrCode_FailedToGetGitHash;
            std::cout << x_ant_warning_prefix(rcode) << "获取代码版本号(SHA-1)失败." << std::endl;
            codeInfo.result = 0;
            return rcode;
        }
        std::string revTxt = x_str_trim(buf);
        if (1 == pm.gH)
        {
            codeInfo.revTxt = revTxt;
            revTxt = revTxt.substr(0, 7);
        }
        else
        {
            codeInfo.revTxt = revTxt.substr(0, 7);
            revTxt = codeInfo.revTxt;
        }
        char *endPtr = xnullptr;
        xuint8 revVal = strtoull(revTxt.c_str(), &endPtr, 16); // wcstoull
        buf[0] = '\0';
#ifdef GS_PLAT_WIN
        sprintf_s(buf, sizeof(buf) / sizeof(buf[0]), "%llu", revVal);
#elif defined(GS_PLAT_LINUX)
        sprintf(buf, "%llu", revVal);
#else
#error  Not supported: sprintf.
#endif
        
        codeInfo.revVal = buf;
    }

    {
        memset(buf, 0, sizeof(buf) / sizeof(buf[0]));
        byteCnt = 0;
        x_str_utf16_2_ansi(buf, sizeof(buf) / sizeof(buf[0]), pm.codePath.c_str());
        codeInfo.localPath = buf;
    }

    {// 获取远端
        std::vector<std::string> remotes;
        memset(buf, 0, sizeof(buf) / sizeof(buf[0]));
        byteCnt = 0;
        wchar_t cmd[] = L"git remote -v"; // 
#ifdef X_OUT_CMD
        std::wcout << x_ant_wprefix() << cmd << std::endl;
#endif //X_OUT_CMD
        ok = x_exec_cmd_by_pipe(cmd, pm.codePath.c_str(), buf, sizeof(buf) / sizeof(buf[0]), byteCnt);
        if (!ok || '\0' == buf[0])
        {
            codeInfo.codePath = ""; // 无远端
        }
        else
        {
            std::string lineTxt;
            const char *cur = buf;
            while ('\0' != *cur)
            {
                lineTxt = std::string();
                while ('\0' != *cur)
                {
                    if ('\r' == *cur)
                    {
                        ++cur;
                        if ('\n' == *cur)
                            ++cur;

                        break;
                    }
                    else if ('\n' == *cur)
                    {
                        ++cur;
                        break;
                    }
                    lineTxt += *cur;
                    ++cur;
                }
                lineTxt = x_str_trim(lineTxt);
                if (lineTxt.empty())
                    continue;

                size_t idx = lineTxt.rfind('(');
                if (-1 != idx)
                {
                    lineTxt = lineTxt.substr(0, idx);
                }

                bool findOK = false;
                for (const auto & var : remotes)
                {
                    if (var == lineTxt)
                    {
                        findOK = true;
                        break;
                    }
                }
                if (!findOK)
                {
                    remotes.push_back(lineTxt);
                }
            }
            for (const auto & var : remotes)
            {
                std::string remote;
                std::string remoteUrl;
                size_t idx = var.find('\t');
                if (-1 != idx)
                {
                    remote = var.substr(0, idx);
                    remoteUrl = x_str_trim(var.substr(idx + 1));

                    remote += "{";
                    remote += remoteUrl;
                    remote += "}";
                }
                else
                {
                    remote = var;
                }
                if (!codeInfo.codePath.empty())
                    codeInfo.codePath += ";";

                codeInfo.codePath += remote;
            }
        }
    }

    codeInfo.result = 1;
    return rcode;
}

xint x_code_svn_info(const xparam & pm, xcodeinfo & codeInfo)
{
    xint rcode = gsrev::ErrCode_OK;
    char buf[65536] = { '\0' };
    unsigned long byteCnt = 0;
    bool ok = false;
    {// 获取svn版本
        memset(buf, 0, sizeof(buf) / sizeof(buf[0]));
        byteCnt = 0;
        wchar_t cmd[] = L"svn --version"; // svn --version --quiet
#ifdef X_OUT_CMD
        std::wcout << x_ant_wprefix() << cmd << std::endl;
#endif //X_OUT_CMD
        ok = x_exec_cmd_by_pipe(cmd, xnullptr, buf, sizeof(buf) / sizeof(buf[0]), byteCnt);
        if (!ok || '\0' == buf[0])
        {
            rcode = gsrev::ErrCode_FailedToGetSvnVersion;
            std::cout << x_ant_warning_prefix(rcode) << "获取svn版本号失败, 可能未安装svn或未配置环境变量." << std::endl;
            codeInfo.result = 0;
            return rcode;
        }
        xint8 idx = x_str_index_of(buf, '\n');
        if (-1 != idx)
            idx = x_str_index_of(buf, '\n', idx + 1);
        if (-1 != idx)
        {
            buf[idx] = '\0';
            if ('\r' == buf[idx - 1])
                buf[idx - 1] = '\0';
        }

        std::string txt(buf);
        std::cout << txt << std::endl; // 正确输出: git version 2.46.1.windows.1 , 否则输出git -v的错误信息

        txt = x_str_trim(txt);
        if (!x_str_starts_with(txt.c_str(), txt.size(), "svn", 3, '\0', x_tolower))
        {
            rcode = gsrev::ErrCode_FailedToGetSvnVersion;
            std::cout << x_ant_warning_prefix(rcode) << "没有发现svn命令." << std::endl;
            codeInfo.result = 0;
            return rcode;
        }
    }

    {// 获取svn info
        memset(buf, 0, sizeof(buf) / sizeof(buf[0]));
        byteCnt = 0;
        wchar_t cmd[] = L"svn info"; // at 或 ct（提交日期）
#ifdef X_OUT_CMD
        std::wcout << x_ant_wprefix() << cmd << std::endl;
#endif //X_OUT_CMD
        ok = x_exec_cmd_by_pipe(cmd, pm.codePath.c_str(), buf, sizeof(buf) / sizeof(buf[0]), byteCnt);
        if (!ok || '\0' == buf[0])
        {
            rcode = gsrev::ErrCode_FailedToGetSvnInfo;
            std::cout << x_ant_warning_prefix(rcode) << "获取代码版本信息失败." << std::endl;
            codeInfo.result = 0;
            return rcode;
        }
    }

    enum  { InfoCnt = 4 };
    xint infoCnt = 0;
    std::string lineTxt;
    const char *cur = buf;
    while ('\0' != *cur)
    {
        lineTxt = std::string();
        while ('\0' != *cur)
        {
            if ('\r' == *cur)
            {
                ++cur;
                if ('\n' == *cur)
                    ++cur;

                break;
            }
            else if ('\n' == *cur)
            {
                ++cur;
                break;
            }

            lineTxt += *cur;
            ++cur;
        }
        lineTxt = x_str_trim(lineTxt);
        if (lineTxt.empty())
            continue;

        if (x_str_starts_with(lineTxt.c_str(), lineTxt.size(), "Working Copy Root Path: ", 24, '\0', x_tolower))
        {
            codeInfo.localPath = lineTxt.substr(24);
            ++infoCnt;
            continue;
        }

        if (x_str_starts_with(lineTxt.c_str(), lineTxt.size(), "URL: ", 5, '\0', x_tolower))
        {
            codeInfo.codePath = lineTxt.substr(5);
            ++infoCnt;
            continue;
        }

        if (x_str_starts_with(lineTxt.c_str(), lineTxt.size(), "Revision: ", 10, '\0', x_tolower))
        {
            codeInfo.revVal = x_str_trim(lineTxt.substr(10));
            char *endPtr = nullptr;
            xint8 revVal = strtoull(codeInfo.revVal.c_str(), &endPtr, 10);
            char xbuf[256] = { '\0' };
#ifdef GS_PLAT_WIN
            sprintf_s(xbuf, sizeof(xbuf) / sizeof(xbuf[0]), "%x", revVal);
#elif defined(GS_PLAT_LINUX)
            sprintf(xbuf, "%x", revVal);
#else
#error  Not supported: sprintf.
#endif
            
            codeInfo.revTxt = xbuf;
            ++infoCnt;
            continue;
        }

        if (x_str_starts_with(lineTxt.c_str(), lineTxt.size(), "Last Changed Date: ", 19, '\0', x_tolower))
        {
            std::string datetime = x_str_trim(lineTxt.substr(19));
            const std::size_t idx = datetime.find_last_of('(');
            if (std::size_t(-1) != idx)
            {
                datetime = datetime.substr(0, idx);
                datetime = x_str_trim(datetime);
            }
            codeInfo.datetime = datetime;
            ++infoCnt;
            continue;
        }
    }

    if (InfoCnt == infoCnt) {
        codeInfo.result = 1;
    }
    else {
        codeInfo.result = 0;
    }
    return 0;
}

bool x_out_file(FILE *tmpl, const xcodeinfo & codeInfo, const std::wstring & outFile, const std::string & buildTime, xint tortoise)
{
    fseek(tmpl, 0, SEEK_SET); // 文件指针复位

    FILE *flOut = xnullptr;
    {
        xchar opm[10] = { L'w', '\0' };
        flOut = x_wfsopen(outFile.c_str(), opm, 0x40/*_SH_DENYNO*/);
    }
    if (xnullptr == flOut)
        return false;

    char lineTxt[65536] = { '\0' };
    while (fgets(lineTxt, sizeof(lineTxt) / sizeof(lineTxt[0]), tmpl))
    {
        std::string strTmp(lineTxt);
        strTmp = x_str_replace_all(strTmp, "$GSBuildDate$", buildTime);
        strTmp = x_str_replace_all(strTmp, "$GSCodeVerMgr$", codeInfo.name);
        strTmp = x_str_replace_all(strTmp, "$GSCodePath$", codeInfo.localPath);
        strTmp = x_str_replace_all(strTmp, "$GSCodeUrl$", codeInfo.codePath);
        strTmp = x_str_replace_all(strTmp, "$GSRevTxt$", codeInfo.revTxt);
        strTmp = x_str_replace_all(strTmp, "$GSRevVal$", codeInfo.revVal);
        strTmp = x_str_replace_all(strTmp, "$GSDate$", codeInfo.datetime);

        if (1 == tortoise)
        {
            strTmp = x_str_replace_all(strTmp, "$WCURL$", codeInfo.codePath);
            strTmp = x_str_replace_all(strTmp, "$WCREV$", codeInfo.revVal);
            strTmp = x_str_replace_all(strTmp, "$WCDATE$", codeInfo.datetime);
        }
#ifdef GS_PLAT_WIN
        fprintf_s(flOut, strTmp.c_str());
#elif defined(GS_PLAT_LINUX)
        fprintf(flOut, strTmp.c_str());
#else
#error  Not supported: fprintf.
#endif
        
        lineTxt[0] = L'\0';
    }
    fclose(flOut);
    lineTxt[0] = '\0';
    x_str_utf16_2_ansi(lineTxt, sizeof(lineTxt) / sizeof(lineTxt[0]), outFile.c_str());
    std::cout << "更新文件:" << lineTxt << std::endl;
    return true;
}

FILE* x_wfsopen(const wchar_t* filename, const wchar_t* mode, int _shflag)
{
#ifdef GS_PLAT_WIN
     return _wfsopen(filename, mode, _shflag);
#elif defined(GS_PLAT_LINUX)
    char m[256]= { '\0' };
    wcstombs(m, mode, sizeof(m) / sizeof(m[0]));
    char filepath[4096] = { '\0' };    
    wcstombs(filepath, filename, sizeof(filepath) / sizeof(filepath[0]));
    return fopen(filepath, m);    
#else
#error  Not supported: fopen().
#endif
}